﻿using System;
using System.Globalization;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using PublicDomain.Framework.UnitTesting.Properties;

namespace PublicDomain.Framework.UnitTesting
{
    public static class AssertExtensions
    {
        public static void Throws(
            Type expectedExceptionType,
            Action testAction,
            string expectedMessage,
            out Exception expectedException)
        {
            expectedException = null;

            try
            {
                testAction();
            }
            catch (Exception exception)
            {
                var exceptionType = exception.GetType();

                if (exceptionType != expectedExceptionType)
                {
                    Assert.Fail(String.Format(
                            CultureInfo.CurrentCulture,
                            Resources.ErrorMessageWrongActualException,
                            expectedExceptionType,
                            exception.GetType()));
                }

                expectedException = exception;

                Assert.AreEqual(expectedMessage, exception.Message);

                return;
            }

            Assert.Fail(String.Format(CultureInfo.CurrentCulture, Resources.ErrorMessageExpectedExceptionHasNotBeenThrown, expectedExceptionType));
        }

        public static void Throws(
           Type expectedExceptionType,
           Action testAction,
           string expectedMessage)
        {
            Exception expectedException;

            Throws(expectedExceptionType, testAction, expectedMessage, out expectedException);
        }

        public static void Throws<TExpectedException>(
            Action testAction,
            string expectedMessage,
            out TExpectedException expectedException) where TExpectedException : Exception
        {
            Exception exception;
            Throws(typeof(TExpectedException), testAction, expectedMessage, out exception);

            expectedException = exception as TExpectedException;
        }

        public static void Throws<TExpectedException>(
            Action testAction,
            string expectedMessage) where TExpectedException : Exception
        {
            TExpectedException expectedException;

            Throws<TExpectedException>(testAction, expectedMessage, out expectedException);
        }

        public static void ThrowsArgumentNullException(
            Action testAction,
            string expectedMessage,
            string expectedParamName,
            out ArgumentNullException expectedException)
        {
            Throws<ArgumentNullException>(testAction, expectedMessage, out expectedException);

            Assert.AreEqual(expectedParamName, expectedException.ParamName);
        }

        public static void ThrowsArgumentNullException(
            Action testAction,
            string expectedParamName)
        {
            ArgumentNullException expectedException;

            var expectedMessage = String.Format(
                CultureInfo.CurrentCulture,
                Resources.DefaultArgumentNullExceptionErrorMessage.Replace("\\r\\n", "\r\n"), 
                expectedParamName);

            Throws<ArgumentNullException>(testAction, expectedMessage, out expectedException);

            Assert.AreEqual(expectedParamName, expectedException.ParamName);
        }

        public static void ThrowsArgumentException(
            Action testAction,
            string expectedMessage,
            string expectedParamName,
            out ArgumentException expectedException)
        {
            Throws<ArgumentException>(testAction, expectedMessage, out expectedException);

            Assert.AreEqual(expectedParamName, expectedException.ParamName);
        }

        public static void Equals(byte[] expected, byte[] actual)
        {
            Assert.AreEqual(expected.Length, actual.Length, Resources.ErrorMessageBytesArrayLengthAssert);

            for (int i = 0; i < actual.Length; i++)
            {
                Assert.AreEqual(expected[i], actual[i], String.Format(CultureInfo.CurrentCulture, Resources.ErrorMessageBytesArrayAssert, i));
            }
        }

        public static void Equals(char[] expected, char[] actual)
        {
            Assert.AreEqual(expected.Length, actual.Length, Resources.ErrorMessageCharsArrayLengthAssert);

            for (int i = 0; i < actual.Length; i++)
            {
                Assert.AreEqual(expected[i], actual[i], String.Format(CultureInfo.CurrentCulture, Resources.ErrorMessageCharsArrayAssert, i));
            }
        }
    }
}
